/*********************************************************************
*                SEGGER MICROCONTROLLER GmbH & Co. KG                *
*        Solutions for real time microcontroller applications        *
**********************************************************************
*                                                                    *
*        (c) 2003-2011     SEGGER Microcontroller GmbH & Co KG       *
*                                                                    *
*        Internet: www.segger.com    Support:  support@segger.com    *
*                                                                    *
**********************************************************************

----------------------------------------------------------------------
----------------------------------------------------------------------
File        : FS_MMC_CM_HW_LPC2478_Olimex_LPC-2478STK.c
Purpose     : Low level MMC/SD driver for the NXP LPC23xx/24xx
---------------------------END-OF-HEADER------------------------------
*/

/*********************************************************************
*
*       Includes
*
**********************************************************************
*/
#include "FS_Int.h"
#include "MMC_SD_CardMode_X_HW.h"

/*********************************************************************
*
*       Defines configurable
*
**********************************************************************
*/
#define PCLK                  (72000000uL / 4)  // Clock speed of MCI peripheral in Hz
#define DMA_CHANNEL           1                 // Index of the DMA channel used for the data transfer
#define MAX_SPEED             50000             // Maximum clock speed of the SD card in KHz

/*********************************************************************
*
*       Defines non configurable
*
**********************************************************************
*/
#define TEMP_BUFFER_ADDR      0x7FD00000  // Temporary buffer for DMA
#define NUM_BLOCKS_AT_ONCE    16          // Maximum number of blocks to read/write in one transfer

/*********************************************************************
*
*       Local defines (sfrs)
*
**********************************************************************
*/
//
// Multimedia Card interface
//
#define MCI_BASE_ADDR   0xE008C000
#define MCI_POWER       (*(volatile U32 *)(MCI_BASE_ADDR + 0x00))
#define MCI_CLOCK       (*(volatile U32 *)(MCI_BASE_ADDR + 0x04))
#define MCI_ARGUMENT    (*(volatile U32 *)(MCI_BASE_ADDR + 0x08))
#define MCI_COMMAND     (*(volatile U32 *)(MCI_BASE_ADDR + 0x0C))
#define MCI_RESP_CMD    (*(volatile U32 *)(MCI_BASE_ADDR + 0x10))
#define MCI_RESP0       (*(volatile U32 *)(MCI_BASE_ADDR + 0x14))
#define MCI_RESP1       (*(volatile U32 *)(MCI_BASE_ADDR + 0x18))
#define MCI_RESP2       (*(volatile U32 *)(MCI_BASE_ADDR + 0x1C))
#define MCI_RESP3       (*(volatile U32 *)(MCI_BASE_ADDR + 0x20))
#define MCI_DATA_TMR    (*(volatile U32 *)(MCI_BASE_ADDR + 0x24))
#define MCI_DATA_LEN    (*(volatile U32 *)(MCI_BASE_ADDR + 0x28))
#define MCI_DATA_CTRL   (*(volatile U32 *)(MCI_BASE_ADDR + 0x2C))
#define MCI_DATA_CNT    (*(volatile U32 *)(MCI_BASE_ADDR + 0x30))
#define MCI_STATUS      (*(volatile U32 *)(MCI_BASE_ADDR + 0x34))
#define MCI_CLEAR       (*(volatile U32 *)(MCI_BASE_ADDR + 0x38))
#define MCI_MASK0       (*(volatile U32 *)(MCI_BASE_ADDR + 0x3C))
#define MCI_FIFO_CNT    (*(volatile U32 *)(MCI_BASE_ADDR + 0x48))
#define MCI_FIFO        (*(volatile U32 *)(MCI_BASE_ADDR + 0x80))
#define MCI_FIFO_ADDR   ( (volatile U32 *)(MCI_BASE_ADDR + 0x80))

//
// System Control Block
//
#define SCB_BASE_ADDR           0xE01FC000
#define PCONP                   (*(volatile U32 *)(SCB_BASE_ADDR + 0x0C4))

//
// General Purpose Input/Output (GPIO)
//
#define GPIO_BASE_ADDR          0xE0028000
#define IODIR1                  (*(volatile U32 *)(GPIO_BASE_ADDR + 0x18))
#define IOSET1                  (*(volatile U32 *)(GPIO_BASE_ADDR + 0x14))

//
// General Purpose Input/Output (FIO)
//
#define FIO_BASE_ADDR           0x3FFFC000
#define FIO2PIN                 (*(volatile U32 *)(FIO_BASE_ADDR + 0x54))
#define FIO4PIN                 (*(volatile U32 *)(FIO_BASE_ADDR + 0x94))

//
// Pin Connect Block
//
#define PINSEL_BASE_ADDR        0xE002C000
#define PINSEL2                 (*(volatile U32 *)(PINSEL_BASE_ADDR + 0x08))
#define PINMODE2                (*(volatile U32 *)(PINSEL_BASE_ADDR + 0x48))

//
// General-purpose DMA Controller
//
#define GPDMA_BASE_ADDR         0xFFE04000
#define GPDMA_INT_TCCLR         (*(volatile U32 *)(GPDMA_BASE_ADDR + 0x008))
#define GPDMA_INT_ERR_STAT      (*(volatile U32 *)(GPDMA_BASE_ADDR + 0x00C))
#define GPDMA_INT_ERR_CLR       (*(volatile U32 *)(GPDMA_BASE_ADDR + 0x010))
#define GPDMA_RAW_INT_TCSTAT    (*(volatile U32 *)(GPDMA_BASE_ADDR + 0x014))
#define GPDMA_RAW_INT_ERR_STAT  (*(volatile U32 *)(GPDMA_BASE_ADDR + 0x018))
#define GPDMA_ENABLED_CHNS      (*(volatile U32 *)(GPDMA_BASE_ADDR + 0x01C))
#define GPDMA_CONFIG            (*(volatile U32 *)(GPDMA_BASE_ADDR + 0x030))
#define GPDMA_CH_SRC            (*(volatile U32 *)(GPDMA_BASE_ADDR + (DMA_CHANNEL * 0x20) + 0x100))
#define GPDMA_CH_DEST           (*(volatile U32 *)(GPDMA_BASE_ADDR + (DMA_CHANNEL * 0x20) + 0x104))
#define GPDMA_CH_LLI            (*(volatile U32 *)(GPDMA_BASE_ADDR + (DMA_CHANNEL * 0x20) + 0x108))
#define GPDMA_CH_CTRL           (*(volatile U32 *)(GPDMA_BASE_ADDR + (DMA_CHANNEL * 0x20) + 0x10C))
#define GPDMA_CH_CFG            (*(volatile U32 *)(GPDMA_BASE_ADDR + (DMA_CHANNEL * 0x20) + 0x110))

//
// MCI status register
//
#define STATUS_DATA_CRC_FAIL    1
#define STATUS_DATA_TIMEOUT     3
#define STATUS_TX_UNDERRUN      4
#define STATUS_RX_OVERRUN       5
#define STATUS_DATA_END         8
#define STATUS_START_BIT_ERR    9

//
// MCI clock register
//
#define CLOCK_ENABLE            8
#define CLOCK_PWR_SAVE          9

//
// DMA channel configuration register
//
#define CH_CFG_ENABLE           0
#define CH_CFG_SRC_PERIPH       1
#define CH_CFG_DEST_PERIPH      6
#define CH_CFG_FLOW_CTRL        11
#define CH_CFG_ACTIVE           17
#define CH_CFG_LOCKED           16
#define CH_CFG_HALT             18

//
// DMA channel control register
//
#define CH_CTRL_SBSIZE          12
#define CH_CTRL_DBSIZE          15
#define CH_CTRL_SWIDTH          18
#define CH_CTRL_DWIDTH          21
#define CH_CTRL_SINC            26
#define CH_CTRL_DINC            27
#define CH_CTRL_TC_EN           31

//
// DMA configuration
//
#define CONFIG_EN               0

/*********************************************************************
*
*       Static data
*
**********************************************************************
*/
static U32 _BlockSize;
static U32 _NumBlocks;
static int _IgnoreCRC;
static U32 _DataControlReg;

/*********************************************************************
*
*       Static code
*
**********************************************************************
*/

/*********************************************************************
*
*       _Delayus
*/
static void _Delayus(unsigned us) {
  volatile unsigned Time;

  Time = us * 70;
  do {} while(--Time);
}

/*********************************************************************
*
*       _ld
*/
static U16 _ld(U32 Value) {
  U16 i;

  for (i = 0; i < 16; i++) {
    if ((1UL << i) == Value) {
      break;
    }
  }
  return i;
}

/*********************************************************************
*
*       _StartDataTransfer
*
*   Function description
*     This fucntion writes the MCI data control registers,
*     and initiates thus the data transfer.
*     The information how to set the register is done in FS_MMC_HW_X_SendCmd().
*/
static void _StartDataTransfer(void) {
  MCI_DATA_LEN  = _BlockSize * _NumBlocks;
  MCI_DATA_CTRL = _DataControlReg;
}

/*********************************************************************
*
*       _DMAInit
*
*   Function description
*     Initialize the DMA.
*     This is necessary since data tranfer thru polling does not work correctly.
*/
static void _DMAInit(void) {
  //
  // Enable GPDMA clock
  //
  PCONP |= (1uL << 29);
  //
  // Clear interrupts on DMA channel 1
  //
  GPDMA_INT_TCCLR   = 1uL << DMA_CHANNEL;
  GPDMA_INT_ERR_CLR = 1uL << DMA_CHANNEL;
  //
  // Enable DMA controller, AHB Master is little endian
  //
  GPDMA_CONFIG      = 1uL << CONFIG_EN;
}

/*********************************************************************
*
*       _DMAStartRead
*
*  Function description
*    Before starting any data tranfers from or to the card
*    we need to program the DMA which will do the work.
*/
static void _DMAStartRead(U32 * pDest, const U32 * pSrc, U32 NumBytes) {
  //
  // Disable the DMA channel if required
  //
  if (GPDMA_ENABLED_CHNS & (1uL << DMA_CHANNEL)) {
    GPDMA_CH_CFG = 1uL << CH_CFG_HALT;
    while (GPDMA_CH_CFG & (1uL << CH_CFG_ACTIVE)) {
      ;
    }
    GPDMA_CH_CFG &= ~(1uL << CH_CFG_ENABLE);
  }
  GPDMA_INT_TCCLR   = 1uL << DMA_CHANNEL;
  GPDMA_INT_ERR_CLR = 1uL << DMA_CHANNEL;
  GPDMA_CH_SRC      = (U32)pSrc;
  GPDMA_CH_DEST     = (U32)pDest;
  GPDMA_CH_CTRL     = 0
                    | ((NumBytes >> 2) & 0x0FFF)    // Transfer words
                    | (2uL << CH_CTRL_SBSIZE)
                    | (2uL << CH_CTRL_DBSIZE)
                    | (2uL << CH_CTRL_SWIDTH)
                    | (2uL << CH_CTRL_DWIDTH)
                    | (1uL << CH_CTRL_DINC)
                    | (1uL << CH_CTRL_TC_EN)
                    ;
  GPDMA_CH_CFG      = 0
                    | (1uL << CH_CFG_ENABLE)
                    | (1uL << CH_CFG_LOCKED)
                    | (4uL << CH_CFG_SRC_PERIPH)    // SD/MMC is the source peripheral
                    | (6uL << CH_CFG_FLOW_CTRL)     // SD/MMC controls the transfer to memory
                    ;
}

/*********************************************************************
*
*       _DMAStartWrite
*
*  Function description
*    Before starting any data tranfers from or to the card
*    we need to program the DMA which will do the work.
*/
static void _DMAStartWrite(U32 * pDest, const U32 * pSrc, U32 NumBytes) {
  //
  // Disable the DMA channel if required
  //
  if (GPDMA_ENABLED_CHNS & (1uL << DMA_CHANNEL)) {
    GPDMA_CH_CFG = 1uL << CH_CFG_HALT;
    while (GPDMA_CH_CFG & (1uL << CH_CFG_ACTIVE)) {
      ;
    }
    GPDMA_CH_CFG &= ~(1uL << CH_CFG_ENABLE);
  }
  GPDMA_INT_TCCLR   = 1uL << DMA_CHANNEL;
  GPDMA_INT_ERR_CLR = 1uL << DMA_CHANNEL;
  GPDMA_CH_SRC      = (U32)pSrc;
  GPDMA_CH_DEST     = (U32)pDest;
  GPDMA_CH_CTRL     = 0
                    | ((NumBytes >> 2) & 0x0FFF)    // Transfer words
                    | (2uL << CH_CTRL_SBSIZE)
                    | (2uL << CH_CTRL_DBSIZE)
                    | (2uL << CH_CTRL_SWIDTH)
                    | (2uL << CH_CTRL_DWIDTH)
                    | (1uL << CH_CTRL_SINC)
                    | (1uL << CH_CTRL_TC_EN)
                    ;
  GPDMA_CH_CFG      = 0
                    | (1uL << CH_CFG_ENABLE)
                    | (1uL << CH_CFG_LOCKED)
                    | (4uL << CH_CFG_DEST_PERIPH)   // SD/MMC is the destination peripheral
                    | (5uL << CH_CFG_FLOW_CTRL)     // SD/MMC controls the transfer to card
                    ;
}

/*********************************************************************
*
*       Public code
*
**********************************************************************
*/

/*********************************************************************
*
*       FS_MMC_HW_X_SetHWNumBlocks
*
*   Function description
*     Sets the number of block (sectors) to be transferred.
*
*/
void FS_MMC_HW_X_SetHWNumBlocks(U8 Unit, U16 NumBlocks) {
  _NumBlocks = NumBlocks;
}

/*********************************************************************
*
*       FS_MMC_HW_X_SetHWBlockLen
*
*   Function description
*     Sets the block size (sector size) that shall be transferred.
*/
void FS_MMC_HW_X_SetHWBlockLen(U8 Unit, U16 BlockSize) {
  _BlockSize = BlockSize;
}

/*********************************************************************
*
*       FS_MMC_HW_X_SetMaxSpeed
*
*   Function description
*     Sets the frequency of the MMC/SD card controller.
*     The frequency is given in kHz.
*     It is called 2 times:
*     1. During card initialization
*        Initialize the frequency to not more than 400kHz.
*     2. After card initialization
*        The CSD register of card is read and the max frequency
*        the card can operate is determined.
*        [In most cases: MMC cards 20MHz, SD cards 25MHz]
*
*/
U16 FS_MMC_HW_X_SetMaxSpeed(U8 Unit, U16 Freq) {
  U32 SDClock;
  int Div;

  //
  // Limit the clock speed if necessary.
  //
  if (Freq > MAX_SPEED) {
    Freq = MAX_SPEED;
  }
  //
  // Formula: MCLCLK frequency = PCLK / (2 * (ClkDiv+1))
  //
  SDClock = PCLK / 1000;
  Div     = (SDClock + ((2 * Freq) - 1)) / (2 * Freq) - 1;
  if (Div < 0) {
    Div = 0;
  }
  MCI_CLOCK  = (1uL << CLOCK_ENABLE)
             | (1uL << CLOCK_PWR_SAVE)
             | Div
             ;
  Freq       = SDClock / (2 * (Div + 1));
  MCI_POWER |= 0x01;    // Bit 1 is set already, from power up to power on
  return Freq;
}

/*********************************************************************
*
*       FS_MMC_HW_X_IsPresent
*
*   Function description
*     Returns the state of the media. If you do not know the state, return
*     FS_MEDIA_STATE_UNKNOWN and the higher layer will try to figure out if
*     a media is present.
*
*   Parameters
*     Unit    Device Index
*
*   Return value
*     FS_MEDIA_STATE_UNKNOWN  The state of the media is unkown
*     FS_MEDIA_NOT_PRESENT    No card is present
*     FS_MEDIA_IS_PRESENT     A card is present
*
*   Note
*     This sample uses a function located in the BSP module of the LPC2478 Embedded Artists eval package.
*/
int FS_MMC_HW_X_IsPresent(U8 Unit) {
  if ((FIO2PIN & (1 << 11)) == 0) {
    return FS_MEDIA_IS_PRESENT;
  }
  return FS_MEDIA_NOT_PRESENT;
}

/*********************************************************************
*
*       FS_MMC_HW_X_IsWriteProtected
*
*   Function description
*     Returns wheter card is write protected or not.
*
*   Return value
*     ==0   Is not write protected
*     ==1   Is write protected
*/
int FS_MMC_HW_X_IsWriteProtected(U8 Unit) {
  if ((FIO4PIN & (1 << 19)) == 0) {
    return 0;  // Is not write protected
  }
  return 1;    // Is write protected
}

/*********************************************************************
*
*       FS_MMC_HW_X_SetResponseTimeOut
*
*   Function description
*     Sets the reponse time out value given in MMC/SD card cycles.
*
*/
void FS_MMC_HW_X_SetResponseTimeOut(U8 Unit, U32 Value) {
  // Fixed in hardware for this controller
}

/*********************************************************************
*
*       FS_MMC_HW_X_SetReadDataTimeOut
*
*   Function description
*     Sets the read data time out value given in MMC/SD card cycles.
*/
void FS_MMC_HW_X_SetReadDataTimeOut(U8 Unit, U32 Value) {
  MCI_DATA_TMR = Value;
}

/*********************************************************************
*
*       FS_MMC_HW_X_SendCmd
*
*   Function description
*     Sends a command to the MMC/SD card.
*/
void FS_MMC_HW_X_SendCmd(U8 Unit, unsigned Cmd, unsigned CmdFlags, unsigned ResponseType, U32 Arg) {
  unsigned ResponseFormat;
  unsigned ldBlockLength;
  volatile int i;

  switch (ResponseType) {
  //
  //  No response is expected
  //
  case FS_MMC_RESPONSE_FORMAT_NONE:
  default:
    ResponseFormat = 0;
    break;
  //
  //  Short response is expected (48bit)
  //
  case FS_MMC_RESPONSE_FORMAT_R1:
  case FS_MMC_RESPONSE_FORMAT_R3:
     ResponseFormat = 1;
    break;
  //
  //  Long response is expected (136bit)
  //
  case FS_MMC_RESPONSE_FORMAT_R2:
    ResponseFormat = 3;
    break;
  }
  if (ResponseType == FS_MMC_RESPONSE_FORMAT_R3) {
    _IgnoreCRC = 1;
  } else {
    _IgnoreCRC = 0;
  }
  ldBlockLength   = _ld(_BlockSize);
  _DataControlReg = (ldBlockLength << 4);
  //
  // Handle the flags;
  //
  if (CmdFlags & FS_MMC_CMD_FLAG_DATATRANSFER) { /* If data transfer */
    _DataControlReg |= (1 << 0)
                    |  (1 << 1)
                    |  (1 << 3)
                    ;
  }
  if (CmdFlags & FS_MMC_CMD_FLAG_WRITETRANSFER) {   /* Write transfer ? */
    _DataControlReg &= ~(1 << 1);   // Set WRITE bit
  }
  if (CmdFlags & FS_MMC_CMD_FLAG_USE_SD4MODE) {   /* 4 bit mode ? */
    MCI_CLOCK |=  (1 << 11);   // Set WIDE bit
  } else {
    MCI_CLOCK &= ~(1 << 11);   // Clear WIDE bit
  }
  if (CmdFlags & FS_MMC_CMD_FLAG_INITIALIZE) {
    volatile U8 NumLoops;

    //
    // Let the clock run to allow the SD card to send the last response.
    //
    MCI_CLOCK &= ~(1uL << CLOCK_PWR_SAVE);
    NumLoops   = 0xFF;
    do {
      ;
    } while (--NumLoops);
    MCI_CLOCK |= 1uL << CLOCK_PWR_SAVE;
  }

  MCI_CLEAR     = 0x7FF;
  MCI_ARGUMENT  = Arg;
  MCI_COMMAND   = (Cmd & 0x3F)            // Command[0:5]
                | (ResponseFormat << 6)   // ResponseFormat [6:7]
                | (1 << 10)               // Enable controller [10]
                ;
}

/*********************************************************************
*
*       FS_MMC_HW_X_GetResponse
*
*   Function description
*     Receives the reponses that was sent by the card after
*     a command was sent to the card.
*/
int FS_MMC_HW_X_GetResponse(U8 Unit, void * pBuffer, U32 Size) {
  unsigned  Status;
  U8      * p;
  int       r;
  int       NumBytes;

  p        = (U8 *)pBuffer;
  NumBytes = Size;
  r        = 0;    // No error so far
  //
  // Wait until command processing is finished
  //
  while (MCI_STATUS & (1 << 11));
  MCI_CLEAR = (1 << 11) | (1 << 7);
  do {
    Status = MCI_STATUS;
    if (Status & (1 << 0) && (_IgnoreCRC == 0)) {
      r = FS_MMC_CARD_RESPONSE_CRC_ERROR;
      MCI_CLEAR = (1 << 0);
      break;
    }
    if (Status & (1 << 2)) {
      r = FS_MMC_CARD_RESPONSE_TIMEOUT;
      MCI_CLEAR = (1 << 2);
      break;
    }
    if (Status & (1 << 6) || _IgnoreCRC) {
      volatile U32 * pReg;

      MCI_CLEAR = (1 << 6) | (1 << 0);
      *p++ = MCI_RESP_CMD;
      NumBytes--;
      pReg = (volatile U32 *)(&MCI_RESP0);
      do {
        U32 Data32;
        Data32 = FS_LoadU32BE((const U8 *)pReg);
        FS_StoreU32LE(p, Data32);
        NumBytes -= 4;
        pReg++;
        p += 4;
      } while (NumBytes >= 4);
      break;
    }
    if (Status & (1 << 7)) {
      MCI_CLEAR = (1 << 7);
      break;
    }
  } while (1);
  return r;
}

/*********************************************************************
*
*       FS_MMC_HW_X_ReadData
*
*   Function description
*     Reads data from MMC/SD card.
*
*   Notes
*     (1) The function uses a temporary buffer located in USB RAM.
*/
int FS_MMC_HW_X_ReadData(U8 Unit, void * pBuffer, unsigned NumBytes, unsigned NumBlocks) {
  U32 * pFifo;
  U32 * pTempBuffer;
  U32   NumBytesToRead;
  U32   Status;

  FS_USE_PARA(Unit);
  FS_USE_PARA(NumBytes);
  FS_USE_PARA(NumBlocks);
  pFifo          = (U32 *)MCI_FIFO_ADDR;
  pTempBuffer    = (U32 *)TEMP_BUFFER_ADDR;
  NumBytesToRead = _BlockSize * _NumBlocks;
  _DMAStartRead(pTempBuffer, pFifo, NumBytesToRead);
  _StartDataTransfer();
  do {
    Status = MCI_STATUS;
    if (Status & (1uL << STATUS_DATA_CRC_FAIL)) {
      return FS_MMC_CARD_READ_CRC_ERROR;
    }
    if (Status & (1uL << STATUS_DATA_TIMEOUT)) {
      return FS_MMC_CARD_READ_TIMEOUT;
    }
    if (Status & (1uL << STATUS_RX_OVERRUN)) {
      return FS_MMC_CARD_READ_GENERIC_ERROR;
    }
    if (Status & (1uL << STATUS_START_BIT_ERR)) {
      return FS_MMC_CARD_READ_GENERIC_ERROR;
    }
    if (GPDMA_RAW_INT_ERR_STAT & (1uL << DMA_CHANNEL)) {
      return FS_MMC_CARD_READ_GENERIC_ERROR;
    }
    if (GPDMA_RAW_INT_TCSTAT & (1uL << DMA_CHANNEL)) {
      break;          // OK, data received
    }
  } while (1);
  FS_MEMCPY(pBuffer, pTempBuffer, NumBytesToRead);      // Note (1)
  return 0;
}

/*********************************************************************
*
*       FS_MMC_HW_X_WriteData
*
*   Function description
*     Writes the data to MMC/SD card.
*
*   Notes
*     (1) The function uses a temporary buffer located in USB RAM.
*
*/
int FS_MMC_HW_X_WriteData(U8 Unit, const void * pBuffer, unsigned NumBytes, unsigned NumBlocks) {
  U32 * pFifo;
  U32 * pTempBuffer;
  U32   NumBytesToWrite;
  U32   Status;

  FS_USE_PARA(Unit);
  FS_USE_PARA(NumBytes);
  FS_USE_PARA(NumBlocks);
  pFifo           = (U32 *)MCI_FIFO_ADDR;
  pTempBuffer     = (U32 *)TEMP_BUFFER_ADDR;
  NumBytesToWrite = _BlockSize * _NumBlocks;
  FS_MEMCPY(pTempBuffer, pBuffer, NumBytesToWrite);     // Note (1)
  _DMAStartWrite(pFifo, pTempBuffer, NumBytesToWrite);
  _StartDataTransfer();
  do {
    Status = MCI_STATUS;
    if (Status & (1uL << STATUS_DATA_CRC_FAIL)) {
      return FS_MMC_CARD_WRITE_CRC_ERROR;
    }
    if (Status & (1uL << STATUS_TX_UNDERRUN)) {
      return FS_MMC_CARD_WRITE_GENERIC_ERROR;
    }
    if (GPDMA_RAW_INT_ERR_STAT & (1uL << DMA_CHANNEL)) {
      return FS_MMC_CARD_WRITE_GENERIC_ERROR;
    }
    if (Status & (1uL << STATUS_DATA_END)) {
      break;        // OK, data sent
    }
  } while (1);
  return 0;
}

/*********************************************************************
*
*       FS_MMC_HW_X_Delay
*
*  Function description
*    Waits for a certain time given by the parameter.
*
*/
void FS_MMC_HW_X_Delay(int ms) {
  _Delayus(ms * 1000);
}

/*********************************************************************
*
*       FS_MMC_HW_X_InitHW
*
*   Function description
*     Initialize the MMC/SD card controller.
*/
void FS_MMC_HW_X_InitHW(U8 Unit) {
  volatile int i;

  FS_USE_PARA(Unit);
  if (MCI_CLOCK & (1 << 8)) {
    MCI_CLOCK &= ~(1 << 8);
  }
  if (MCI_POWER & 0x02) {
    MCI_POWER = 0x00;
  }
  for (i = 0; i < 0x1000; i++);
  PCONP |= ( 1 << 28 );     // Enable clock to the MCI block
  //
  // Disable all interrupts for now
  //
  MCI_MASK0 = 0;
  //
  // Connect MCI signals to P1.02, P1.03, P1.06, P1.07, P1.11 and P1.12
  //
  PINSEL2 &= ~((0x3 <<  4) |
               (0x3 <<  6) |
               (0x3 << 10) |
               (0x3 << 12) |
               (0x3 << 14) |
               (0x3 << 22) |
               (0x3 << 24));
  PINSEL2 |=   (0x2 <<  4) |  // MCICLK
               (0x2 <<  6) |  // MCICMD
               (0x2 << 12) |  // MCIDAT0
               (0x2 << 14) |  // MCIDAT1
               (0x2 << 22) |  // MCIDAT2
               (0x2 << 24) ;  // MCIDAT3
  //
  // Setup P1.5 as GPIO and set as output and set to high.
  // This will then enable the MCI Power on board.
  //
  PINSEL2 &= ~(3 << 10);
  IODIR1  |=  (1 <<  5);
  IOSET1   =  (1 <<  5);
  //
  // Set up clocking default mode, clear any registers as needed
  //
  MCI_COMMAND = 0;
  MCI_DATA_CTRL = 0;
  MCI_CLEAR = 0x7FF;    // Clear all pending interrupts
  MCI_POWER = 0x02;     // Power up
  while ((MCI_POWER & 0x02) == 0);
  _DMAInit();
  for ( i = 0; i < 0x1000; i++ );
}

/*********************************************************************
*
*       FS_MMC_HW_X_GetMaxReadBurst
*
*   Function description
*     Returns the number of block (sectors)
*     that can be read at once with a single
*     READ_MULTIPLE_SECTORS.
*
*/
U16 FS_MMC_HW_X_GetMaxReadBurst(U8 Unit) {
  FS_USE_PARA(Unit);
  return NUM_BLOCKS_AT_ONCE;
}

/*********************************************************************
*
*       FS_MMC_HW_X_GetMaxWriteBurst
*
*   Function description
*     Returns the number of block (sectors)
*     that can be written at once with a single
*     WRITE_MULTIPLE_SECTORS.
*/
U16 FS_MMC_HW_X_GetMaxWriteBurst(U8 Unit) {
  FS_USE_PARA(Unit);
  return NUM_BLOCKS_AT_ONCE;
}

/*********************************************************************
*
*       FS_MMC_HW_X_SetDataPointer
*
*   Function description
*     Tells the hardware layer where to read data from
*     or write data to. This may be necessary for some controller,
*     before sending the command to the card, eg. programming the DMA.
*     In most cases this function can be left empty.
*
*   Parameters
*     Unit  SD card controller no, in case there are more than one.
*
*/
void FS_MMC_HW_X_SetDataPointer(U8 Unit, const void * p) {
  FS_USE_PARA(Unit);
  FS_USE_PARA(p);
}

/*************************** End of file ****************************/

